/*
 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
 *
 * SPDX-License-Identifier: GPL-2.0-only
 */

#pragma once

#include <config.h>
#include <types.h>
#include <plat/machine.h>
#include <arch/smp/ipi.h>

#ifdef ENABLE_SMP_SUPPORT
#define MAX_IPI_ARGS    3   /* Maximum number of parameters to remote function *//*Z IPI远程调用参数最大数量 */
/*Z IPI同步信号量 */
static volatile struct {
    word_t count;       /*Z 已完成cpu计数 */
    word_t globalsense; /*Z 全部结束标志。反转触发 */

    PAD_TO_NEXT_CACHE_LN(sizeof(word_t) + sizeof(word_t));
} ipiSyncBarrier = {0};                  /* IPI barrier for remote call synchronization */
/*Z 远程调用IPI是在持有锁的情况下发出的，没有别人写这些全局变量；其它核在试图获取锁期间可以处理IPI */
static volatile word_t totalCoreBarrier; /* number of cores involved in IPI 'in progress' *//*Z IPI远程调用目标cpu数量 */
static word_t ipi_args[MAX_IPI_ARGS];    /* data to be passed to the remote call function *//*Z IPI远程调用参数 */
/*Z 获取第n个IPI参数。无锁的情况下硬读这些全局变量，因为持有锁并设置变量的核已设置完毕并正在阻塞等待别的核来处理 */
static inline word_t get_ipi_arg(word_t n)
{
    assert(n < MAX_IPI_ARGS);
    return ipi_args[n];
}
/*Z 等待所有的核处理完毕(全局信号量加1，当信号量=参数时说明都完成了在等待，最后完成的反转信号统一结束) */
static inline void ipi_wait(word_t cores)
{
    word_t localsense = ipiSyncBarrier.globalsense;     /*Z 取得标志当前值 */
        /*Z GCC内建函数：原子加并返回原值 */
    if (__atomic_fetch_add(&ipiSyncBarrier.count, 1, __ATOMIC_ACQ_REL) == cores) {
        ipiSyncBarrier.count = 0;                       /*Z 完成数+1，最后完成的重置信号量 */
        ipiSyncBarrier.globalsense =
            ~ipiSyncBarrier.globalsense;
    }

    while (localsense == ipiSyncBarrier.globalsense) {  /*Z 反转意味着OK */
        arch_pause();
    }
}
/*Z 根据指定的cpu索引位图一一发送指定的IPI(物理寻址模式) */
/* Architecture independent function for sending handling pre-hardware-send IPIs */
void generic_ipi_send_mask(irq_t ipi, word_t mask, bool_t isBlocking);
/*Z 根据指定的cpu索引位图发送指定的IPI */
/* An architecture/platform should implement this function either as a wrapper to
 * its own arch_ipi_send_mask() or using the generic_ipi_send_mask() function
 * provided to be architecture agnostic.
 */
void ipi_send_mask(irq_t ipi, word_t mask, bool_t isBlocking);

/* Hardware implementation for sending IPIs */
void ipi_send_target(irq_t irq, word_t cpuTargetList);

/* This function switches the core it is called on to the idle thread,
 * in order to avoid IPI storms. If the core is waiting on the lock, the actual
 * switch will not occur until the core attempts to obtain the lock, at which
 * point the core will capture the pending IPI, which is discarded.

 * The core who triggered the store is responsible for triggering a reschedule,
 * or this call will idle forever */
void ipiStallCoreCallback(bool_t irqPath);
/*Z 处理IPI。irqPath为真表示是中断引起，为假表示是系统调用引起 */
/* IPIs could be handled, both using hardware interrupts and software flag
 * in CLH lock. 'irqPath' is used to differentiate the caller path, i.e.
 * if it is called while waiting on the lock to handle the IRQ or not. The
 * remote call handler, would decide if 'handleIPI' should return base
 * on this value, as IRQs could be re/triggered asynchronous */
void handleIPI(irq_t irq, bool_t irqPath);
/*Z 设置IPI远程调用功能号、参数，发送IPI，等待所有的核处理完毕。mask为目标cpu索引掩码 */
/*
 * Run a synchronous function on all cores specified by mask. Return when target cores
 * have all executed the function. Caller must hold the lock.
 *
 * @param func the function to run
 * @param data1 passed to the function as first parameter
 * @param data2 passed to the function as second parameter
 * @param mask cores to run function on
 */
void doRemoteMaskOp(IpiRemoteCall_t func, word_t data1, word_t data2, word_t data3, word_t mask);
/*Z 向指定cpu发送指定功能号、参数的远程调用IPI，并等待其处理完毕 */
/* Run a synchronous function on a core specified by cpu.
 *
 * @param func the function to run
 * @param data1 passed to the function as first parameter
 * @param data2 passed to the function as second parameter
 * @param cpu core to run function on
 */
static void inline doRemoteOp(IpiRemoteCall_t func, word_t data1, word_t data2, word_t data3, word_t cpu)
{
    doRemoteMaskOp(func, data1, data2, data3, BIT(cpu));
}

/* List of wrapper functions
 *
 * doRemote[Mask]Op0Arg: do remote operation without any argument
 * doRemote[Mask]Op1Arg: do remote operation with one argument
 * doRemote[Mask]Op2Arg: do remote operation with two arguments
 * These should be used in favour of directly calling 'doRemote[Mask]Op'
 * in case arguments change in future.
 *
 * @param func the function to run
 * @param data passed to the function as parameters
 * @param cpu[mask] cores to run function on
 */
static void inline doRemoteMaskOp0Arg(IpiRemoteCall_t func, word_t mask)
{
    doRemoteMaskOp(func, 0, 0, 0, mask);
}

static void inline doRemoteMaskOp1Arg(IpiRemoteCall_t func, word_t data1, word_t mask)
{
    doRemoteMaskOp(func, data1, 0, 0, mask);
}
/*Z 设置IPI远程调用功能号、参数，发送IPI，等待所有的核处理完毕。mask为目标cpu索引掩码 */
static void inline doRemoteMaskOp2Arg(IpiRemoteCall_t func, word_t data1, word_t data2, word_t mask)
{
    doRemoteMaskOp(func, data1, data2, 0, mask);
}

static void inline doRemoteMaskOp3Arg(IpiRemoteCall_t func, word_t data1, word_t data2, word_t data3, word_t mask)
{
    doRemoteMaskOp(func, data1, data2, data3, mask);
}
/*Z 向指定cpu发送指定功能号、无参数的远程调用IPI，并等待其处理完毕 */
static void inline doRemoteOp0Arg(IpiRemoteCall_t func, word_t cpu)
{
    doRemoteOp(func, 0, 0, 0, cpu);
}
/*Z 向指定cpu发送指定功能号、1个参数的远程调用IPI，并等待其处理完毕 */
static void inline doRemoteOp1Arg(IpiRemoteCall_t func, word_t data1, word_t cpu)
{
    doRemoteOp(func, data1, 0, 0, cpu);
}

static void inline doRemoteOp2Arg(IpiRemoteCall_t func, word_t data1, word_t data2, word_t cpu)
{
    doRemoteOp(func, data1, data2, 0, cpu);
}

static void inline doRemoteOp3Arg(IpiRemoteCall_t func, word_t data1, word_t data2, word_t data3, word_t cpu)
{
    doRemoteOp(func, data1, data2, data3, cpu);
}

/* This is asynchronous call and could be called outside the lock.
 * Returns immediately.
 *
 * @param mask cores to request rescheduling
 */
void doMaskReschedule(word_t mask);

/* Request rescheduling on a core specified by cpu.
 * Returns immediately.
 *
 * @param cpu core to reschedule
 */
static void inline doReschedule(word_t cpu)
{
    if (cpu != getCurrentCPUIndex()) {
        assert(cpu < CONFIG_MAX_NUM_NODES);
        doMaskReschedule(BIT(cpu));
    }
}

#endif /* ENABLE_SMP_SUPPORT */

